package org.spiderflow.core.expression.parsing.ast.operation;


import org.spiderflow.core.expression.ExpressionError;
import org.spiderflow.core.expression.ExpressionTemplate;
import org.spiderflow.core.expression.ExpressionTemplateContext;
import org.spiderflow.core.expression.parsing.ast.Expression;
import org.spiderflow.core.expression.parsing.enums.UnaryOperator;
import org.spiderflow.core.expression.parsing.token.Token;

import java.io.IOException;

/**
 * An unary operation node represents a logical or numerical negation.
 *
 * @author zhaoshihao
 * @version 2021/11/9 14:33
 */
public class UnaryOperation extends Expression {
    private final UnaryOperator operator;
    private final Expression operand;

    public UnaryOperation(Token operator, Expression operand) {
        super(operator.getSpan());
        this.operator = UnaryOperator.getOperator(operator);
        this.operand = operand;
    }

    public UnaryOperator getOperator() {
        return operator;
    }

    public Expression getOperand() {
        return operand;
    }

    @Override
    public Object evaluate(ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
        Object operandEvaluate = getOperand().evaluate(template, context);
        if (getOperator() == UnaryOperator.NEGATE) {
            if (operandEvaluate instanceof Integer) {
                return -(Integer) operandEvaluate;
            } else if (operandEvaluate instanceof Float) {
                return -(Float) operandEvaluate;
            } else if (operandEvaluate instanceof Double) {
                return -(Double) operandEvaluate;
            } else if (operandEvaluate instanceof Byte) {
                return -(Byte) operandEvaluate;
            } else if (operandEvaluate instanceof Short) {
                return -(Short) operandEvaluate;
            } else if (operandEvaluate instanceof Long) {
                return -(Long) operandEvaluate;
            } else {
                throw ExpressionError.error("Operand of operator '" + getOperator().name() + "' must be a number, got " + operand, getSpan());
            }
        } else if (getOperator() == UnaryOperator.NOT) {
            if (!(operandEvaluate instanceof Boolean)) {
                throw ExpressionError.error("Operand of operator '" + getOperator().name() + "' must be a boolean", getSpan());
            }
            return !(Boolean) operandEvaluate;
        } else {
            return operandEvaluate;
        }
    }
}
